//+--------------------------------------------------------------------------------
//| Copyright (c) 2012 - 2013 ### an.vuongngocduy
//| This file is a part of Compact Game Engine (CGE)
//+--------------------------------------------------------------------------------
#include "cgeImage.h"

namespace cge
{
	namespace gfx
	{
		//=========================================================================================
		Image::Image()
		{
			mDesc.Data = null;
			mDesc.Dimension.Set(0, 0);
			mDesc.Format = PixelFormat::Unknown;
		}

		//=========================================================================================
		Image::Image(const Image& img)
		{
			*this = img;
		}

		//=========================================================================================
		Image::Image(EPixelFormat format, const Dimension2u& dimension)
		{
			mDesc.Data = null;
			mDesc.Dimension = dimension;
			mDesc.Format = format;
			u32 imgSize = Size();
			mDesc.Data = mAlloc.Allocate(imgSize);
			if(mDesc.Data == null) //out of memory
			{
				mDesc.Dimension.Set(0,0);
				mDesc.Format = PixelFormat::Unknown;
			}
			memset(mDesc.Data, imgSize, 0);
		}

		//=========================================================================================
		Image::Image(const u8* data, EPixelFormat format,  const Dimension2u& dimension)
		{
			mDesc.Data = null;
			if(data == null) return;		
			mDesc.Dimension = dimension;
			mDesc.Format = format;
			u32 imgSize = Size();
			mDesc.Data = mAlloc.Allocate(imgSize);
			if(mDesc.Data == null) //out of memory
			{
				mDesc.Dimension.Set(0,0);
				mDesc.Format = PixelFormat::Unknown;
				return;
			}
			memcpy((void*)mDesc.Data, (void*) data, imgSize);
		}

		//=========================================================================================
		Image::Image(u8* data, EPixelFormat format,  const Dimension2u& dimension, b8 useExternalMem)
		{
			mDesc.Data = null;
			if(data == null) return;		
			mDesc.Dimension = dimension;
			mDesc.Format = format;
			u32 imgSize = Size();
			if(useExternalMem)
			{
				mDesc.Data = mAlloc.Allocate(imgSize);
				if(mDesc.Data == null) //out of memory
				{
					mDesc.Dimension.Set(0,0);
					mDesc.Format = PixelFormat::Unknown;
					return;
				}
				memcpy((void*)mDesc.Data, (void*) data, imgSize);
			}
			else
			{
				data[imgSize - 1]; //access to the end of input buffer to make sure it is not corrupted
				mDesc.Data = data;
			}
		}

		//=========================================================================================
		Image::~Image()
		{
			Dispose();
		}

		//=========================================================================================
		EPixelFormat Image::PixelFormat() const
		{	
			return mDesc.Format;
		}

		//=========================================================================================
		u32 Image::BitsPerPixel() const
		{
			switch(mDesc.Format)
			{
			case PixelFormat::Bpp16_GreyScale:
			case PixelFormat::Bpp16_R5G6B5:
			case PixelFormat::Bpp16_A1R5G5B5:
			case PixelFormat::Bpp16_A4R4G4B4:	
				return 16;

			case PixelFormat::Bpp24_R8G8B8:
				return 24;

			case PixelFormat::Bpp32_A8R8G8B8:
				return 32;

			default: 
				return 0; //error
			}
		}

		//=========================================================================================
		u32 Image::Size() const
		{
			u32 bytesPerPixel = BitsPerPixel() / 8;
			u32 size = mDesc.Dimension.Width * mDesc.Dimension.Height * bytesPerPixel;
			return size;
		}

		//=========================================================================================
		Dimension2u Image::Dimension() const
		{
			return mDesc.Dimension;
		}

		//=========================================================================================
		u32 Image::Width() const
		{
			return mDesc.Dimension.Width;
		}

		//=========================================================================================
		u32 Image::Height() const
		{
			return mDesc.Dimension.Height;
		}

		//=========================================================================================
		const u8* Image::Data() const
		{
			return mDesc.Data;
		}

		u8* Image::Data()
		{
			return mDesc.Data;
		}

		//=========================================================================================
		Color Image::GetPixel(u32 x, u32 y) const
		{
			Color clr;
			if(x < mDesc.Dimension.Width && y < mDesc.Dimension.Height)
			{
				u32 bytesPerPixel = BitsPerPixel() / 8;
				const u8* px = mDesc.Data + (bytesPerPixel * (mDesc.Dimension.Width * y + x));
				if(mDesc.Format == PixelFormat::Bpp32_A8R8G8B8)
					clr.Set(px[0], px[1], px[2], px[3]);
				else if(mDesc.Format == PixelFormat::Bpp24_R8G8B8)
					clr.Set(px[0], px[1], px[2], 0xFF);
			}
			return clr;
		}

		//=========================================================================================
		Image& Image::SetPixel(const Color& col, u32 x, u32 y)
		{
			return *this;
		}

		//=========================================================================================
		Image& Image::FlipVertical()
		{
			u32 bytesPerPixel = BitsPerPixel() / 8;
			u32 bytesPerRow = mDesc.Dimension.Width * bytesPerPixel;
			u8* tmp = mAlloc.Allocate(bytesPerRow);
			for(u32 i = 0, j = mDesc.Dimension.Height - 1; i < j ; i++ , j--)
			{
				memcpy(tmp, mDesc.Data + (i * bytesPerRow), bytesPerRow);
				memcpy(mDesc.Data + (i * bytesPerRow), mDesc.Data + (j * bytesPerRow), bytesPerRow);
				memcpy(mDesc.Data + (j * bytesPerRow), tmp, bytesPerRow);
			}
			mAlloc.Deallocate(tmp);
			return *this;
		}

		//=========================================================================================
		Image& Image::FlipHorizontal()
		{
			u32 bytesPerPixel = BitsPerPixel() / 8;
			u32 bytesPerRow = mDesc.Dimension.Width * bytesPerPixel;
			u8* tmp = mAlloc.Allocate(bytesPerRow);
			for(u32 i = 0; i < mDesc.Dimension.Height; i++)
			{
				u8* nextRowData = mDesc.Data + (i * bytesPerRow);
				for(u32 j  = 0, k = mDesc.Dimension.Width - 1; j < k; j++ , k--)
					memcpy(tmp + (j * bytesPerPixel), nextRowData + (k * bytesPerPixel), bytesPerPixel);
				memcpy(nextRowData, tmp, bytesPerRow); 
			}
			mAlloc.Deallocate(tmp);
			return *this;
		}

		//=========================================================================================
		void Image::Dispose()
		{
			if(mDesc.Data)
				mAlloc.Deallocate(mDesc.Data);
			mDesc.Data = null;
		}

		//=========================================================================================
		Image& Image::operator = (const Image& img)
		{
			if(mDesc.Data == img.mDesc.Data)
				return *this;

			u32 size = img.Size();

			if(size != Size())
			{
				Dispose();
				mDesc.Data = mAlloc.Allocate(size);
			}

			mDesc.Format = img.mDesc.Format;
			mDesc.Dimension = img.mDesc.Dimension;
			memcpy(mDesc.Data, img.mDesc.Data, size);
		}

		//=========================================================================================
	}
}